<?php
/**
 * Created by PhpStorm.
 * User: wen
 * Date: 2021/7/4
 * Time: 11:16
 */

namespace WenRuns\Laravel\Admin;

use Encore\Admin\Facades\Admin;
use Encore\Admin\Grid;
use Illuminate\Database\Eloquent\Model;
use WenRuns\Laravel\Button;
use WenRuns\Laravel\Laravel;

class GridService
{
    /**
     * @var null | \WenRuns\Laravel\Admin\Grid |Grid
     */
    protected $grid = null;


    /**
     * @var null
     */
    protected $actionsClosure = [
        'closure' => null,
        'options' => ['edit' => true, 'view' => true, 'delete' => true],
    ];

    /**
     * @var bool
     */
    protected $exporter = false;

    /**
     * @var null | \Closure
     */
    protected $filterClosure = null;


    protected $floatScrollbar = true;


    /**
     *
     * GridService constructor.
     * @param $modelClass
     * @param string $girdClass
     */
    public function __construct($modelClass, $girdClass = \WenRuns\Laravel\Admin\Grid::class)
    {
        if (is_string($modelClass)) {
            $modelClass = new $modelClass;
        }
        $this->grid = new $girdClass($modelClass);
    }

    /**
     * 隐藏批量操作
     * @param bool $disable
     * @return $this
     */
    public function disableBatchActions($disable = true)
    {
        $this->grid->disableBatchActions($disable);
        return $this;
    }

    /**
     * 隐藏Action操作列
     * @param bool $disable
     * @return $this
     */
    public function disableActions($disable = true)
    {
        $this->grid->disableActions($disable);
        return $this;
    }

    /**
     * 隐藏创建按钮
     * @param bool $disable
     * @return $this
     */
    public function disableCreateButton($disable = true)
    {
        $this->grid->disableCreateButton($disable);
        return $this;
    }

    /**
     * 关闭Filter筛选
     * @param bool $disable
     * @return $this
     */
    public function disableFilter($disable = true)
    {
        $this->grid->disableFilter($disable);
        return $this;
    }

    /**
     * 相同内容合并 - 字段
     * @param string $columns
     * @param string $javascriptFn
     * @param int $mergeInput
     * @return $this
     */
    public function mergeColspan($columns = '*', $javascriptFn = 'false', $mergeInput = 2)
    {
        $this->grid->mergeColspan(...func_get_args());
        return $this;
    }

    /**
     * @param \Closure $closure
     * @return $this
     */
    public function where(\Closure $closure)
    {
        $closure->call($this, $this->grid->model());
        return $this;
    }

    /**
     * grid表格内容
     * @param \Closure $closure
     * @return $this
     */
    public function content(\Closure $closure)
    {
        call_user_func($closure, $this->grid, $this);
        return $this;
    }

    /**
     * filter筛选
     * @param \Closure $closure
     * @return $this
     */
    public function filter(\Closure $closure)
    {
        $this->filterClosure = $closure;
        return $this;
    }

    /**
     * tools工具
     * @param \Closure $closure
     * @return $this
     */
    public function tools(\Closure $closure)
    {
        if (is_callable($closure)) {
            $this->grid->tools($closure);
        }
        return $this;
    }

    /**
     * 规格筛选器
     * @param \Closure|null $closure
     * @return $this
     */
    public function selector(\Closure $closure = null)
    {
        if (is_callable($closure)) {
            $this->grid->selector($closure);
        }
        return $this;
    }

    /**
     * 脚部内容
     * @param \Closure|null $closure
     * @return $this
     */
    public function footer(\Closure $closure = null)
    {
        if (is_callable($closure)) {
            $this->grid->footer(function ($model) use ($closure) {
                return $closure->call($this, $model);
            });
        }
        return $this;
    }


    /**
     * 头部内容
     * @param \Closure|null $closure
     * @return $this
     */
    public function header(\Closure $closure = null)
    {
        if (is_callable($closure)) {
            $this->grid->header($closure);
        }
        return $this;
    }

    /**
     * Action操作列
     * @param \Closure|null|array $actions
     * @param array $options
     * @return $this
     */
    public function actions($actions = null, $options = [])
    {
        $this->actionsClosure = [
            'closure' => $actions,
            'options' => $options,
        ];

        return $this;
    }

    /**
     * 导出
     * @param $exporter
     * @return $this
     */
    public function exporter($exporter)
    {
        if (is_callable($exporter)) {
            $exporter = $exporter->call($this);
        }
        if ($exporter) {
            $this->grid->exporter($exporter);
            $this->exporter = true;
        }
        return $this;
    }

    /**
     * 隐藏空页面
     * @param bool $disable
     * @return $this
     */
    public function disableDefineEmptyPage($disable = true)
    {
        $this->grid->disableDefineEmptyPage($disable);
        return $this;
    }

    /**
     * 隐藏Tools工具栏
     * @param bool $disable
     * @return $this
     */
    public function disableTools($disable = true)
    {
        $this->grid->disableTools($disable);
        return $this;
    }

    /**
     * 隐藏字段选择器
     * @param bool $disable
     * @return $this
     */
    public function disableColumnSelector($disable = true)
    {
        $this->grid->disableColumnSelector($disable);
        return $this;
    }

    /**
     * 不使用分页选择器
     * @param bool $disable
     * @return $this
     */
    public function disablePerPageSelector($disable = true)
    {
        $this->grid->disablePerPageSelector($disable);
        return $this;
    }

    /**
     * 不使用分页
     * @param bool $disable
     * @return $this
     */
    public function disablePagination($disable = true)
    {
        $this->grid->disablePagination($disable);
        return $this;
    }

    /**
     * 检查导出 筛选
     * @return Grid|mixed|null
     */
    protected function checkInit()
    {
        $this->exporter || $this->grid->disableExport();
        $filterClosure = $this->filterClosure;
        $this->grid->filter(function (Grid\Filter $filter) use ($filterClosure) {
            $filter->disableIdFilter();
            $filterClosure && $filterClosure->call($this, $filter);
        });
        return $this->grid;
    }

    /**
     * 追加script
     * @param $script
     * @return $this
     */
    public function script($script)
    {
        if (is_callable($script)) {
            $script = $script->call($this);
        }
        $script = preg_replace(
            ["/([^:])\s*\/\/[\s\S]*?[\r\n\t]+/m", "/\s*<!--[\s\S]*?-->[\r\n\t]*|\s*\/\*[\s\S]*?\*\/[\r\n\t]*/m"],
            ["$1\n", "\n"],
            $script
        );
        Admin::script($script);
        return $this;
    }

    /**
     * 追加css
     * @param $css
     * @param $isFile
     * @return $this
     */
    public function css($css, $isFile = false)
    {
        if ($isFile) {
            Admin::css($css . '?t=' . mt_rand(10000, 99999));
        } else {
            if (is_callable($css)) {
                $css = $css->call($this);
            }
            $path = '/vendor/wenruns/laravel-service/assets/css';
            $dirPath = public_path($path);
            if (!is_dir($dirPath)) {
                mkdir($dirPath, 0777, true);
                chmod($dirPath, 0777);
            }
            $filepath = $dirPath . '/dynamic_css.css';
            file_put_contents($filepath, $css);
            Admin::css($path . "/dynamic_css.css?t=" . mt_rand(1000, 9999));
        }
        return $this;
    }

    /**
     * 追加js
     * @param $jsPath
     * @return $this
     */
    public function js($jsPath)
    {
        if (is_array($jsPath)) {
            foreach ($jsPath as $path) {
                if (strpos($path, '?') === false) {
                    $path .= '?t=' . mt_rand(1000, 9999);
                } else {
                    $path .= '&t=' . mt_rand(1000, 9999);
                }
                Admin::js($path);
            }
        } else {
            if (strpos($jsPath, '?') === false) {
                $jsPath .= '?t=' . mt_rand(1000, 9999);
            } else {
                $jsPath .= '&t=' . mt_rand(1000, 9999);
            }
            Admin::js($jsPath);
        }
        return $this;
    }

    /**
     * 批量操作
     * @param false $disableDelete
     * @param \Closure|null $closure
     * @return $this
     */
    public function batchActions($disableDelete = false, \Closure $closure = null)
    {
        $this->grid->batchActions(function (Grid\Tools\BatchActions $batchActions) use ($disableDelete, $closure) {
            $disableDelete && $batchActions->disableDelete();
            is_callable($closure) && $closure->call($this, $batchActions);
        });
        return $this;
    }


    /**
     * @param bool $float
     * @return $this
     */
    public function disableFloatScrollbar($disable = true)
    {
        $this->floatScrollbar = !$disable;
        return $this;
    }


    /**
     * 输出模板
     * @return Grid|mixed|null
     */
    public function render()
    {
        if ($this->grid->option('show_define_empty_page')) {
            self::showEmptyPage();
        }
        $actionsOption = $this->actionsClosure;
        $this->checkInit()
            ->actions(function (Grid\Displayers\Actions $actions) use ($actionsOption) {
                $closure = $actionsOption['closure'] ?? null;
                $options = $actionsOption['options'] ?? [];
                if (is_array($closure)) {
                    if (is_callable($options)) {
                        $temp = $options;
                        $options = $closure;
                        $closure = $temp;
                    } else {
                        $options += $closure;
                        $closure = null;
                    }
                }
                $enableEdit = $options['edit'] ?? empty($closure) && empty($options);
                $enableView = $options['view'] ?? empty($closure) && empty($options);
                $enableDelete = $options['delete'] ?? empty($closure) && empty($options);
                $enableView || $actions->disableView();
                $enableDelete || $actions->disableDelete();
                $enableEdit || $actions->disableEdit();
                if (is_callable($closure)) {
                    $closure->call($this, $actions);
                }
            });
        $this->checkFloatScrollbar();

        return $this->grid;
    }


    /**
     * 浮动滚动条
     * @return $this
     */
    protected function checkFloatScrollbar(): GridService
    {
        if ($this->floatScrollbar) {
            Laravel::loadJs('grid/grid.js');
            $js = Laravel::makeAssetLoadUrl('grid/grid.js');
            $script = <<<SCRIPT
if(typeof FloatScrollbar == "undefined"){
    let script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "{$js}";
    script.onload = ()=>{
        window.floatScrollBar = new FloatScrollbar({
            el: ".table-wrap.table-main",
        });
    }
    document.head.append(script);
}else{
    window.floatScrollBar = new FloatScrollbar({
        el: ".table-wrap.table-main",
    });
}
SCRIPT;
            Admin::script($script);
        }
        return $this;
    }

    /**
     * 空表格
     * @param string $selector
     */
    public static function showEmptyPage($selector = '.table-wrap.table-main')
    {
        $emptyPage = self::emptyPage();
        $script = <<<SCRIPT
$(function(){
    var mTb = document.querySelectorAll("{$selector}");
    if(mTb){
        mTb.forEach((tb, i)=>{
            if(tb.querySelector("tbody").children.length<=0){
                tb.innerHTML += `{$emptyPage}`;
            }
        });
    }
});
SCRIPT;
        Admin::script($script);
    }

    /**
     * 空页面
     * @return string
     */
    protected static function emptyPage()
    {
        return <<<HTML
<table class="table table-hover grid-table">
    <tr>
        <td colspan="2" class="empty-grid" style="padding: 100px;text-align: center;color: #999999">
            <svg t="1562312016538" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2076" width="128" height="128" style="fill: #e9e9e9;">
                <path d="M512.8 198.5c12.2 0 22-9.8 22-22v-90c0-12.2-9.8-22-22-22s-22 9.8-22 22v90c0 12.2 9.9 22 22 22zM307 247.8c8.6 8.6 22.5 8.6 31.1 0 8.6-8.6 8.6-22.5 0-31.1L274.5 153c-8.6-8.6-22.5-8.6-31.1 0-8.6 8.6-8.6 22.5 0 31.1l63.6 63.7zM683.9 247.8c8.6 8.6 22.5 8.6 31.1 0l63.6-63.6c8.6-8.6 8.6-22.5 0-31.1-8.6-8.6-22.5-8.6-31.1 0l-63.6 63.6c-8.6 8.6-8.6 22.5 0 31.1zM927 679.9l-53.9-234.2c-2.8-9.9-4.9-20-6.9-30.1-3.7-18.2-19.9-31.9-39.2-31.9H197c-19.9 0-36.4 14.5-39.5 33.5-1 6.3-2.2 12.5-3.9 18.7L97 679.9v239.6c0 22.1 17.9 40 40 40h750c22.1 0 40-17.9 40-40V679.9z m-315-40c0 55.2-44.8 100-100 100s-100-44.8-100-100H149.6l42.5-193.3c2.4-8.5 3.8-16.7 4.8-22.9h630c2.2 11 4.5 21.8 7.6 32.7l39.8 183.5H612z" p-id="2077"></path>
            </svg>
        </td>
    </tr>
</table>
HTML;
    }


    public function statistic($columns, $options = [])
    {
        $columns = json_encode((array)$columns);
        $trAttributes = json_encode($options['trAttributes'] ?? []);
        $tableSelector = $options['tableSelector'] ?? '.table.grid-table';
        $format = json_encode($options['format'] ?? []);
        $handles = json_encode($options['handles'] ?? []);
        $text = $options['text'] ?? '汇总';
        $script = <<<SCRIPT
$(function(){
    function gridAppendTotalTr(){
        var tableObj = document.querySelectorAll("{$tableSelector} tbody");
        tableObj.forEach((body, i)=>{
            if(body.querySelector(".empty-grid")){
                return ;
            }
            var totalObj = {}, columns={$columns}, format = {$format}, handles = {$handles};
            var trDom = document.createElement("tr"), attributes = {$trAttributes};
            Object.keys(attributes).forEach(function(value, key){
                trDom.setAttribute(value, attributes[value]);
            });
            columns.forEach(function(column, i){
                if(handles[column]){
                    eval("var handle = " + handles[column]);
                    handle(totalObj, column);
                }else{
                    totalObj[column] = 0;
                }
            });

            Array.from(body.children).forEach(function(vo, k){
                columns.forEach(function(column, i){
                    if(handles[column]){
                        eval("var handle = " + handles[column]);
                        handle(totalObj, column, vo);
                    }else{
                        var obj = vo.querySelector(".column-"+column),
                            number = Number(obj ? obj.innerHTML.replace("%", "") : 0);
                        totalObj[column] += number;
                    }
                });
            });
            body.append(trDom);
            Array.from(body.children[0].children).forEach(function(vo, k){
                var tdDom = document.createElement("td");
                Array.from(vo.attributes).forEach(function(attr, i){
                    tdDom.setAttribute(attr.name, attr.value);
                });
                trDom.append(tdDom);
                if(k== 0 && columns.indexOf(vo.classList.value.replace("column-", "")) < 0){
                    tdDom.innerHTML = '{$text}';
                }else{
                    for(var i in columns){
                        var classList = Array.from(vo.classList), column = columns[i];
                        if(classList.indexOf("column-" + column)>=0){
                            var value = Math.round(totalObj[column] * 100)/100;
                            if(format[column]){
                                eval(`var callback = `+format[column]);
                                value = callback(totalObj, body);
                            }
                            tdDom.innerHTML = value;
                            break;
                        }else{
                            tdDom.innerHTML = '--';
                        }
                    }
                }

            });
        });
    }
    gridAppendTotalTr();
});
SCRIPT;
        Admin::script($script);
        return $this;
    }


    /**
     * 实例化当前对象
     * @param $modelClass
     * @param string $gridClass
     * @return static
     */
    public static function instance($modelClass, $gridClass = \WenRuns\Laravel\Admin\Grid::class)
    {
        return new static($modelClass, $gridClass);
    }

    /**
     * @param $name
     * @param $arguments
     * @return mixed
     */
    public function __call($name, $arguments)
    {
        return $this->grid->$name(...$arguments);
    }
}
